package com.example.tcpseverdemo;

import android.Manifest;
import android.content.pm.PackageManager;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private TcpSever mTcpSever;
    private ExecutorService exec;
    private Button btnOn;
    private Button btnPlay;
    private FiFoUsbStream fiFoUsbStream;
    private LinkedList<ByteBuffer> frameBufferQueue;
    private PlayerThreadSec playerThreadSec;
    private SurfaceView sv;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnOn = findViewById(R.id.btn_on);
        btnOn.setOnClickListener(this);
        btnPlay = findViewById(R.id.btn_play);
        btnPlay.setOnClickListener(this);
        sv = findViewById(R.id.sv);
        checkPermission();
        exec = Executors.newCachedThreadPool();
        fiFoUsbStream = new FiFoUsbStream();
        frameBufferQueue = new LinkedList<ByteBuffer>();
        initSurfaceView();

    }

    private void initSurfaceView() {
        sv.setVisibility(View.VISIBLE);
        SurfaceHolder holder = sv.getHolder();
        holder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                //初始化解码器
                if (playerThreadSec == null) {
                    int width = holder.getSurfaceFrame().width();
                    int height = holder.getSurfaceFrame().height();
                    playerThreadSec = new PlayerThreadSec(holder.getSurface(), width, height);
                    playerThreadSec.start();
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                if (playerThreadSec == null) {
                    playerThreadSec.interrupt();
                    stopCodec();
                }
            }
        });
    }


    /**
     * android6.0之后要动态获取权限
     */
    private void checkPermission() {
        // Storage Permissions
        final int REQUEST_EXTERNAL_STORAGE = 1;
        String[] PERMISSIONS_STORAGE = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
        try { //检测是否有写的权限
            int permission = ActivityCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE");
            if (permission != PackageManager.PERMISSION_GRANTED) {
                // 没有写的权限，去申请写的权限，会弹出对话框
                ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

    }

    int flag = 0;

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_on:
                if (flag == 0) {
                    mTcpSever = new TcpSever(5600, fiFoUsbStream);
                    exec.execute(mTcpSever);
                    btnOn.setText("关闭");
                    flag = 1;
                } else {
                    mTcpSever.closeSelf();
                    exec.shutdown();
                    btnOn.setText("开启");
                    flag = 0;
                }
                break;
            case R.id.btn_play:
                getH264FrameData();
                break;
            default:
                break;
        }
    }


    private void getH264FrameData() {
        exec.execute(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    if (fiFoUsbStream.getActualSize() > 1 * 1024) {
                        searchFrame(fiFoUsbStream);
                    }
                }
            }
        });
    }

    int flag1 = 0;
    int countFlag = 0;
    int frameSize = 0;

    private void searchFrame(FiFoUsbStream fiFoUsbStream) {
        int i = 0;
        int len = fiFoUsbStream.getActualSize();
        for (i = 0; i < len; i++) {
            if (fiFoUsbStream.buffer[(fiFoUsbStream.front + i) % fiFoUsbStream.FIFO_SIZE] == (byte) 0x00 && i + 4 < fiFoUsbStream.getActualSize()
                    && fiFoUsbStream.buffer[(fiFoUsbStream.front + i + 1) % fiFoUsbStream.FIFO_SIZE] == (byte) 0x00) {
                if (
                        fiFoUsbStream.buffer[(fiFoUsbStream.front + i + 2) % fiFoUsbStream.FIFO_SIZE] == (byte) 0x00 &&
                                fiFoUsbStream.buffer[(fiFoUsbStream.front + i + 3) % fiFoUsbStream.FIFO_SIZE] == (byte) 0x01) {
                    if (fiFoUsbStream.buffer[(fiFoUsbStream.front + i + 4) % fiFoUsbStream.FIFO_SIZE] == (byte) 0x67 ||
                            fiFoUsbStream.buffer[(fiFoUsbStream.front + i + 4) % fiFoUsbStream.FIFO_SIZE] == (byte) 0x41) {
                        countFlag++;
                        if (countFlag == 1) {
                            flag1 = i;

                            i += 64;
                            frameSize += 64;

                        } else if (countFlag == 2) {
                            byte[] dataa = new byte[frameSize + flag1];
                            fiFoUsbStream.FiFoRead(dataa, frameSize + flag1);
                            ByteBuffer byteBuffer = ByteBuffer.wrap(dataa, flag1, frameSize);
                            pushFrameBufferSec(byteBuffer);
                            flag1 = 0;
                            countFlag = 0;
                            frameSize = 0;
                            break;
                        }
                    }
                }
            }
            if (countFlag == 1) {
                frameSize++;
            }
        }
        flag1 = 0;
        countFlag = 0;
        frameSize = 0;
    }

    private void pushFrameBufferSec(ByteBuffer byteBuffer) {
        try {
            synchronized (frameBufferQueue) {
                byteBuffer.clear();
                byte[] Buffer = new byte[byteBuffer.capacity()];
                byteBuffer.get(Buffer, 0, Buffer.length);
                ByteBuffer buffer = ByteBuffer.wrap(Buffer, 0, Buffer.length);
                frameBufferQueue.add(buffer);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean hasFrameBufferSec() {
        synchronized (frameBufferQueue) {
            return !frameBufferQueue.isEmpty();
        }
    }

    private int frameBufferSizeSec() {
        return frameBufferQueue.size();
    }

    private ByteBuffer dequeueFrameBufferSec() {
        synchronized (frameBufferQueue) {
            return frameBufferQueue.removeFirst();
        }
    }

    private class PlayerThreadSec extends Thread {

        private Surface surface;
        private int width;
        private int height;

        public PlayerThreadSec(Surface surface, int w, int h) {
            this.surface = surface;
            this.width = w;
            this.height = h;
        }

        @Override
        public void run() {
            try {
                decoder = MediaCodec.createDecoderByType("video/avc");
            } catch (IOException e) {
                e.printStackTrace();
            }
            MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);

            mediaFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, width);
            mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, height);
            mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, width * height);
            mediaFormat.setInteger(MediaFormat.KEY_PUSH_BLANK_BUFFERS_ON_STOP, 1);
            byte[] sps = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x67, (byte) 0x42, (byte) 0x40, (byte) 0x28, (byte) 0xA6, (byte) 0x80, (byte) 0x78, (byte) 0x02, (byte) 0x27, (byte) 0xE5, (byte) 0x40};
            byte[] pps = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x68, (byte) 0xCE, (byte) 0x30, (byte) 0xA4, (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x65, (byte) 0x88};
            mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(sps));
            mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(pps));

            decoder.configure(mediaFormat, surface, null, 0);
            decoder.start();

            ByteBuffer buffer = null;

            try {
                while (true) {
                    if (hasFrameBufferSec()) {

                        buffer = dequeueFrameBufferSec();
                        if (buffer != null) {
                            offerDecoderBufferSec(buffer);
                            buffer = null;
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private MediaCodec decoder = null;
    private int mFrameIndexSec = 0;

    private void offerDecoderBufferSec(ByteBuffer byteBuffer) {
        try {
            ByteBuffer[] inputBuffers = decoder.getInputBuffers();
            int inputBufferIndex = decoder.dequeueInputBuffer(-1);
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                long timestamp = mFrameIndexSec++ * (1000000 / 30);
                inputBuffer.rewind();
                inputBuffer.put(byteBuffer);

                decoder.queueInputBuffer(inputBufferIndex, 0, byteBuffer.capacity(), timestamp, 0);
            }

            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            int outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
            while (outputBufferIndex >= 0) {
                decoder.releaseOutputBuffer(outputBufferIndex, true);
                outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    public void stopCodec() {
        try {
            decoder.stop();
            decoder.release();
            decoder = null;
        } catch (Exception e) {
            e.printStackTrace();
            decoder = null;
        }
    }

}
